function getShapeDir(num: number): string {
	return `shape/char/${miscUtils.stringPad(num, 4)}`;
}

function getCharFile(num: number): string {
	const file = `shape/charani/${miscUtils.stringPad(num, 4)}.json`;
	return file;
}
function getFullCharFile(num: number): string {
	const full = assets.getFullPath(getCharFile(num));
	return full;
}

function newAniAddFile(data: any, file: string, shape: number): void {
	const basename: string = path.basename(file);
	const index = basename.indexOf('_');
	const name = basename.substr(0, index);

	console.log('newAniAddFile', shape, name);
	const json = FsUtils.loadJson(file);
	if (json && json.mc && json.mc['0']) {
		data[name] = json.mc['0'];
		const { frames } = json.mc['0'];
		data[name].ani = name;
		for (const f of frames) {
			delete f.x;
			delete f.y;
		}
	}
}

function fastAniData(data2: MCData): MCData {
	for (const ani of data2.frames) {
		if (ani.duration > 1) {
			ani.duration--;
		}
	}
	return data2;
}

// function subArray(src: any[], start: number, end: number): any[] {
// 	return [...src].splice(start, end - start);
// }

// function leftArray(src: any[], num: number): any[] {
// 	return [...src].splice(0, num);
// }

// function rightArray(src: any[], num: number): any[] {
// 	return [...src].splice(src.length-num);
// }

// function joinArray(src: any[], frames: number[]): any[] {
// 	const len = frames.length;
// 	let out = [];
// 	for (let i = 0; i < len / 2; i++) {
// 		const f = subArray(src, frames[2 * i], frames[2 * i + 1]);
// 		out = [...out, ...f];
// 	}
// 	return out;
// }

function appendAniDefault(mc: Record<string, MCData>, ani: string, src: string): boolean {
	if (!mc[src]) {
		return false;
	}
	if (mc[ani]) {
		return true;
	}

	const datasrc: MCData = Utils.deepCopy(mc[src]);

	const len = datasrc.frames.length;

	if (ani === 'rjumpl') {
		console.log('appendAniDefault', ani, src);
	}

	if (
		(ani === 'relife' && src === 'die') ||
		(ani === 'rjump' && src === 'jump') ||
		(ani === 'rjumpl' && src === 'jumpl') ||
		ani === 'hitup'
	) {
		reverseAnieData(datasrc);
	}
	if (ani === 'stand0' && src === 'stand') {
		datasrc.frames = [datasrc.frames[0]];
	}

	if (1) {
		// jump
		if ((ani === 'jumpup' && src === 'jump') || (ani === 'rjumpup' && src === 'rjump')) {
			datasrc.frames = datasrc.frames.left(7);
		} else if (
			(ani === 'jumpfly' && src === 'jump') ||
			(ani === 'rjumpfly' && src === 'rjump')
		) {
			datasrc.frames = [datasrc.frames[7]];
		} else if (
			(ani === 'jumpdown' && src === 'jump') ||
			(ani === 'rjumpdown' && src === 'rjump')
		) {
			datasrc.frames.splice(0, 8);
		} else if (ani === 'jumpl' && src === 'jump') {
			datasrc.frames = datasrc.frames.joinArray([0, 3, 12, len]);
		} else if (
			(ani === 'jumplup' && src === 'jumpl') ||
			(ani === 'rjumplup' && src === 'rjumpl')
		) {
			datasrc.frames = datasrc.frames.left(2);
		} else if (
			(ani === 'jumplfly' && src === 'jumpl') ||
			(ani === 'rjumplfly' && src === 'rjumpl')
		) {
			datasrc.frames = [datasrc.frames[2]];
		} else if (
			(ani === 'jumpldown' && src === 'jumpl') ||
			(ani === 'rjumpldown' && src === 'rjumpl')
		) {
			datasrc.frames.splice(0, 3);
		}
	}

	if (1) {
		// attack
		if (ani === 'attackb' || ani === 'attack2b' || ani === 'magicb') {
			datasrc.frames.splice(len - 6);
		} else if (ani === 'attackm' || ani === 'attack2m' || ani === 'magicm') {
			datasrc.frames = datasrc.frames.splice(len - 6, 4);
		} else if (ani === 'attacke' || ani === 'attack2e' || ani === 'magice') {
			datasrc.frames.splice(0, len - 2);
		} else if (ani === 'attacktwo' || ani === 'attack2two' || ani === 'magictwo') {
			datasrc.frames = datasrc.frames.joinArray([
				0,
				len - 6,
				len - 6,
				len - 2,
				len - 6,
				len - 2,
				len - 2,
				len,
			]);
		} else if (ani === 'attackthree' || ani === 'attack2three' || ani === 'magicthree') {
			datasrc.frames = datasrc.frames.joinArray([
				0,
				len - 6,
				len - 6,
				len - 2,
				len - 6,
				len - 2,
				len - 6,
				len - 2,
				len - 2,
				len,
			]);
		}
	}
	if (ani === 'hitdown') {
		datasrc.frames = datasrc.frames.left(len / 2);
	}

	if (ani === 'attackq' || ani === 'attack2q' || ani === 'magicq' || ani === 'walkq') {
		fastAniData(datasrc);
	}

	mc[ani] = datasrc;
	mc[ani].ani = datasrc.ani || src;
	return true;
}

// 从目录自动生成ani
function newAni(shape: number, save: boolean): MCFile {
	const dir = getShapeDir(shape);
	const full = assets.getFullPath(dir);
	const files = FsUtils.findFilesInDir(full, '_0.json', false);

	ui.msglog('newAni', shape, dir, files.length);

	const data: MCFile = { mc: {}, res: {} };
	if (files.length === 0) {
		return data;
	}

	for (const f of files) {
		// console.log(f);
		newAniAddFile(data.mc, f, shape);
	}

	// 如果有攻击attack，就生产一系列的动作
	const anis = Object.keys(data.mc);
	if (anis.includes('attack')) {
		const defaultanis = {
			attack2: 'attack',

			attackl: 'attack', // light
			attackh: 'attack', // heavy
			attackq: 'attack', // quick
			attacktwo: 'attack',
			attackthree: 'attack',
			attackb: 'attack',
			attackm: 'attack',
			attacke: 'attack',

			attack2l: 'attack2',
			attack2h: 'attack2',
			attack2q: 'attack2',
			attack2two: 'attack2',
			attack2three: 'attack2',
			attack2b: 'attack',
			attack2m: 'attack',
			attack2e: 'attack',

			magicl: 'magic', // light
			magich: 'magic', // heavy
			magicq: 'magic', // quick

			relife: 'die',

			hitdown: 'die',
			hitup: 'hitdown',
			hitfly: 'die',
			hitground: 'die',
		};
		for (const [ani, from] of Object.entries(defaultanis)) {
			appendAniDefault(data.mc, ani, from);
		}
	}

	// 主角有jump
	if (anis.includes('jump')) {
		const defaultanis = {
			walkq: 'walk',
			stand0: 'stand',

			jumpup: 'jump',
			jumpfly: 'jump',
			jumpdown: 'jump',

			jumpl: 'jump',
			jumplup: 'jumpl',
			jumpldown: 'jumpl',
			jumplfly: 'jumpl',

			rjump: 'jump',
			rjumpup: 'rjump',
			rjumpdown: 'rjump',
			rjumpfly: 'rjump',

			rjumpl: 'jumpl',
			rjumplup: 'rjumpl',
			rjumpldown: 'rjumpl',
			rjumplfly: 'rjumpl',
		};
		for (const [ani, from] of Object.entries(defaultanis)) {
			appendAniDefault(data.mc, ani, from);
		}
	} else if (anis.includes('walk')) {
		const defaultanis = {
			walkq: 'walk',
			stand0: 'stand',
		};
		for (const [ani, from] of Object.entries(defaultanis)) {
			appendAniDefault(data.mc, ani, from);
		}
	}

	if (save) {
		const jsonfile = getCharFile(shape);
		assets.saveJsonByUrl(jsonfile, data);
	}

	return data;
}

function moveShapeFile(file: string, x: number, y: number) {
	const json = FsUtils.loadJson(file) as MCFile;
	if (json && json.mc && json.mc['0']) {
		for (const res of Object.values(json.res)) {
			delete res['kx'];
			delete res['ky'];
			res.offx -= x;
			res.offy -= y;
		}

		for (const data of Object.values(json.mc)) {
			for (const f of data.frames) {
				f.x -= x;
				f.y -= y;
			}
		}
		FsUtils.saveJson(file, json);
	}
}

function moveShape(shape: number, x: number, y: number) {
	const dir = getShapeDir(shape);
	const full = assets.getFullPath(dir);
	const files = FsUtils.findFilesInDir(full, '.json', false);
	for (const f of files) {
		// console.log('moveShape', f);
		moveShapeFile(f, x, y);
	}
	ui.msglog(`Moved ${shape} files ${files.length}`);
}

const AniFrame2 = ['attack', 'attack2', 'jump', 'magic', 'rwalk'];

function isAniFrame2(base: string) {
	for (const ani of AniFrame2) {
		if (base.indexOf(ani) === 0) {
			return true;
		}
	}
	return false;
}

function aniShapeDefaultFile(file: string) {
	const json = FsUtils.loadJson(file) as MCFile;

	if (json && json.mc && json.mc['0']) {
		const frame = isAniFrame2(path.basename(file)) ? 2 : 3;

		for (const data of Object.values(json.mc)) {
			for (const f of data.frames) {
				if ((f.duration || 0) < frame) {
					f.duration = frame;
				}
			}
		}

		FsUtils.saveJson(file, json);
	}
}

function aniShapeDefault(shape: number) {
	const dir = getShapeDir(shape);
	const full = assets.getFullPath(dir);
	const files = FsUtils.findFilesInDir(full, '.json', false);
	for (const f of files) {
		console.log('aniShapeDefault', f);
		aniShapeDefaultFile(f);
	}
	ui.msglog(`aniShapeDefault ${shape} files ${files.length}`);
}

function resizeMv(data: MCFile, scale: number): MCFile {
	for (const mc of Object.values(data.mc)) {
		for (const f of mc.frames) {
			f.x = Math.round(f.x * scale);
			f.y = Math.round(f.y * scale);
		}
	}
	for (const f of Object.values(data.res)) {
		f.x = Math.round(f.x * scale);
		f.y = Math.round(f.y * scale);
		f.w = Math.round(f.w * scale);
		f.h = Math.round(f.h * scale);
		if (f.offx != null) {
			f.offx = Math.round(f.offx * scale);
			f.offy = Math.round(f.offy * scale);
		}
	}
	return data;
}

async function mvResize(json: string, scale: number) {
	const data = FsUtils.loadJson(json);
	if (!data) {
		return false;
	}

	const png = `${json.substr(0, json.length - 4)}png`;
	if (!fs.existsSync(png)) {
		return false;
	}

	const imp = await Jimp.create(png);
	const w = imp.getWidth();
	const h = imp.getHeight();
	const w2 = Math.ceil(w * scale);
	const h2 = Math.ceil(h * scale);

	console.log('mvResize', json, scale, w, h, w2, h2);

	imp.resize(w2, h2, (err, image) => {
		if (!err) {
			image.write(png);
			const d = resizeMv(data, scale);
			FsUtils.saveJson(json, d);
		}
	});

	return true;
}

function scaleShape(shape: number, scale: number) {
	const dir = getShapeDir(shape);
	const full = assets.getFullPath(dir);
	const files = FsUtils.findFilesInDir(full, '.json', false);
	for (const f of files) {
		console.log('scaleShape', f);
		mvResize(f, scale);
	}
	ui.msglog(`Scaled ${shape} files ${files.length}`);
}
